/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package ru.dusty.fs.importfs;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Date;
import java.util.List;
import ru.dusty.fs.filesystem.data.Block;
import ru.dusty.fs.filesystem.data.BlockType;
import ru.dusty.fs.filesystem.data.BlocksMap;
import ru.dusty.fs.filesystem.data.MetaData;
import ru.dusty.fs.filesystem.exceptions.FileAlreadyExistsException;
import ru.dusty.fs.filesystem.utils.PrimitiveSizes;
import ru.dusty.fs.filesystem.utils.Utils;

/**
 *
 * @author Dusty
 */
public class ImportFs {
    
    private static final String NAME = "import";
    
    private BlocksMap map;
    
    private void importFile(String root, File hostFile, List<String> path) 
            throws FileAlreadyExistsException, FileNotFoundException, OutOfMemoryError, IOException {
        
        MetaData data;
        Block block = Utils.getFirstBlock(root, path);
        if (block == null) {
            String fileName = path.get(path.size() - 1);
            path.remove(path.size() - 1);
            Utils.createDir(root, map, path);
            Block parentBlock = Utils.getFirstBlock(root, path);
            path.add(fileName);

            data = new MetaData();
            data.setName(fileName);
            data.setType(BlockType.FILE);

            int blockNum = map.allocateBlock();
            if (blockNum == -1) {
                throw new OutOfMemoryError("Not enough memory blocks.");
            }
            block = new Block(new File(root + File.separator + blockNum));
            block.writeMetaData(data);
            block.setNextBlock(0);

            Utils.addToDir(root, map, parentBlock, blockNum);
        } else {
            data = new MetaData();
            block.readMetaData(data);
            
            if (data.getType().equals(BlockType.FOLDER)) 
                throw new FileAlreadyExistsException(Utils.listToPath(path));
            
            data.setSize(0);
            block.writeMetaData(data);
            
            int nextBlock = block.getNextBlock();
            while (nextBlock != 0) {
                Block tempBlock = new Block(new File(root + File.separator + nextBlock));
                map.deallocateBlock(nextBlock);
                nextBlock = tempBlock.getNextBlock();
            }
            block.setNextBlock(0);
        }
        
        RandomAccessFile raf = new RandomAccessFile(hostFile, "r");
        byte[] fileData = new byte[(int) raf.length()];
        raf.read(fileData);
        
        int allData = (int) (raf.length() + data.getMetaDataSize());
        int sizeInBlock = Utils.getBlockSize(root) - PrimitiveSizes.sizeof(allData);
        int blocksCount = allData / sizeInBlock;
        if (allData % sizeInBlock != 0) blocksCount++;
        raf.close();
        
        int offset = sizeInBlock - data.getMetaDataSize();
        block.seek(data.getMetaDataSize());
        if (offset > fileData.length) {
            block.writeData(fileData, 0, fileData.length);
        } else {
            block.writeData(fileData, 0, sizeInBlock - data.getMetaDataSize());
        }
        Block firstBlock = block;
        Block prevBlock = block;
        for (int i = 1; i < blocksCount; i++) {
            int blockNum = map.allocateBlock();
            if (blockNum == -1) {
                throw new OutOfMemoryError("Not enough memory blocks.");
            }
            block = new Block(new File(root + File.separator + blockNum));
            prevBlock.setNextBlock(blockNum);
            block.seek(0);
            if (sizeInBlock > fileData.length - offset) {
                block.writeData(fileData, offset, fileData.length - offset);
            } else {
                block.writeData(fileData, offset, sizeInBlock);
            }
            offset += sizeInBlock;
            prevBlock = block;
        }
        block.setNextBlock(0);
        
        data.setLastUpdDate(new Date());
        data.setSize(fileData.length);
        firstBlock.writeMetaData(data);
    }
    
    public int importFs(String[] args) {
        if (args.length < 3) {
            System.out.println(NAME + ": missing operand after \"" + NAME + "\"");
            System.out.println(NAME + ": use \"" + NAME + " root host_file fs_file\"");
            return 1;
        }
        
        String root = args[0];
        File hostFile = new File(args[1]);
        
        if (!hostFile.isFile() || !hostFile.canRead()) {
            System.out.println(NAME + ": " + hostFile.getPath() + ": file does not exist or is unavailable.");
            return 6;
        }
        
        try {
            if (!Utils.isFsFormatted(root)) {
                System.out.println(NAME + ": File system error.");
                System.out.println(NAME + ": Use the command \"format root\"");
                return 5;
            }
            
            List<String> path = Utils.parsePath(args[2]);
            if (path.isEmpty()) {
                System.out.println(NAME + ": missing the target file name.");
                return 6;
            }
            
            System.out.println(NAME + ": Importing the file...");
            
            map = new BlocksMap(root, Utils.getBlocksCount(root));
            map.initMap();
            
            importFile(root, hostFile, path);
        } catch (FileAlreadyExistsException ex) {
            System.out.println(NAME + ": Unable to create the specified directory.");
            System.out.println(NAME + ": " + ex.getFile() + ": such file or folder already existed.");
            return 7;
        } catch (FileNotFoundException ex) {
            System.out.println(NAME + ": Could not find the file block.");
            System.out.println(NAME + ": " + ex.getMessage());
            return 2;
        } catch (OutOfMemoryError ex) {
            System.out.println(NAME + ": Memory error.");
            System.out.println(NAME + ": " + ex.getMessage());
            return 8;
        } catch (IOException ex) {
            System.out.println(NAME + ": Error occurred while reading the file block.");
            System.out.println(NAME + ": " + ex.getMessage());
            return 3;
        }
        
        System.out.println(NAME + ": Importing completed successfully.");
        
        return 0;
    }
}
